menutrackeritem: protect against use-after-free
authorChristian Hergert <chergert@redhat.com>
Fri, 24 Jun 2022 21:28:18 +0000 (14:28 -0700)
committerChristian Hergert <chergert@redhat.com>
Fri, 24 Jun 2022 21:30:45 +0000 (14:30 -0700)
With recent updates to GLib, I now see cases where we can hit a state that
has finalized before notify (which will bump the ref count back up). This
is evident in GNOME Text Editor when showing a language submenu from a
popover, and then dismissing the popover and subsequently the tab.

With the previous commit, we at least get a warning like this, which helped
track down the issue.

 Gtk-CRITICAL **: gtk_action_observable_unregister_observer: assertion 'GTK_IS_ACTION_OBSERVABLE (observable)' failed
 GLib-GObject-CRITICAL **: g_object_ref: assertion '!object_already_finalized' failed

This patch fixes both of those criticals.

Fixes #5009

gtk/gtkmenutrackeritem.c

index 11db1f4abb361ba63961e9cd10014cbaec92d055..8b459c7a41871f2340a3f09cdddf9b03068f0d8c 100644 (file)
@@ -870,16 +870,25 @@ gtk_menu_tracker_opener_finalize (GObject *object)
 {
   GtkMenuTrackerOpener *opener = (GtkMenuTrackerOpener *)object;
 
-  gtk_action_observable_unregister_observer (opener->item->observable,
-                                             opener->submenu_action,
-                                             (GtkActionObserver *)opener);
+  if (opener->item != NULL)
+    {
+      GtkMenuTrackerItem *item = g_object_ref (opener->item);
+
+      g_clear_weak_pointer (&opener->item);
+
+      gtk_action_observable_unregister_observer (item->observable,
+                                                 opener->submenu_action,
+                                                 (GtkActionObserver *)opener);
 
-  if (GTK_IS_ACTION_MUXER (opener->item->observable))
-    gtk_action_muxer_change_action_state (GTK_ACTION_MUXER (opener->item->observable),
-                                          opener->submenu_action,
-                                          g_variant_new_boolean (FALSE));
+      if (GTK_IS_ACTION_MUXER (item->observable))
+        gtk_action_muxer_change_action_state (GTK_ACTION_MUXER (item->observable),
+                                              opener->submenu_action,
+                                              g_variant_new_boolean (FALSE));
 
-  gtk_menu_tracker_item_set_submenu_shown (opener->item, FALSE);
+      gtk_menu_tracker_item_set_submenu_shown (item, FALSE);
+
+      g_object_unref (item);
+    }
 
   g_clear_pointer (&opener->submenu_action, g_free);
 
@@ -992,7 +1001,8 @@ gtk_menu_tracker_opener_new (GtkMenuTrackerItem *item,
   opener = g_object_new (gtk_menu_tracker_opener_get_type (), NULL);
 
   opener->first_time = TRUE;
-  opener->item = item;
+
+  g_set_weak_pointer (&opener->item, item);
 
   if (item->action_namespace)
     opener->submenu_action = g_strjoin (".", item->action_namespace, submenu_action, NULL);